package com.iplay.web.order;

import java.text.ParseException;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.iplay.component.date.FeastBookingDateFormatter;
import com.iplay.configuration.security.jwtAuthentication.auth.UserContext;
import com.iplay.dto.ApiResponse;
import com.iplay.dto.ApiResponseMessage;
import com.iplay.dto.cashback.CashbackDTO;
import com.iplay.dto.cashback.CashbackStrategyCustomizationDTO;
import com.iplay.dto.order.OrderDTO;
import com.iplay.dto.order.SimplifiedOrderForAdminDTO;
import com.iplay.entity.order.ApprovalStatus;
import com.iplay.entity.order.ApprovalStatus.ModifiableApprovalStatus;
import com.iplay.entity.order.OrderStatus;
import com.iplay.service.exception.InvalidRequestParametersException;
import com.iplay.service.exception.ResourceNotFoundException;
import com.iplay.service.order.OrderService;
import com.iplay.service.review.ReviewService;
import com.iplay.service.totp.TOTPService;
import com.iplay.service.user.SimplifiedUser;
import com.iplay.vo.cashback.CashbackStrategyVO;
import com.iplay.vo.order.PostContractVO;
import com.iplay.vo.order.PostPaymentVO;
import com.iplay.vo.order.PostReservationVO;
import com.iplay.vo.order.query.OrderQueryVO;
import com.iplay.vo.review.PostReviewVO;
import com.iplay.web.configuration.PaginationConfig;
import com.iplay.web.configuration.VOConstraints;
import com.iplay.web.exception.BadRequestException;

import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import springfox.documentation.annotations.ApiIgnore;

@RestController
@RequestMapping("/api/orders")
public class OrderController {
	@Autowired
	private OrderService orderService;

	@Autowired
	private FeastBookingDateFormatter dateFormatter;

	@Autowired
	private TOTPService totpService;
	
	@Autowired
	private ReviewService reviewService;

	@ApiOperation(notes = "用户新建一个咨询单，返回包含订单id的ApiResponse。如果ApiResponse为false，（1）该推介人用户不存在。（2）验证码不正确", value = "")
	@PostMapping
	@PreAuthorize("hasAnyRole('USER', 'MANAGER')")
	public ApiResponse<?> postOrder(@Valid @ApiParam("咨询单实体") @RequestBody PostReservationVO vo,
			@ApiIgnore @AuthenticationPrincipal UserContext context) {
		if (!totpService.validateTOTPUsingDefaultSecretKey(vo.getTotp()))
			return ApiResponse.createFailApiResponse(ApiResponseMessage.TOTP_INVALID);
		SimplifiedUser authenticatedUser = new SimplifiedUser(context.getUserId(), context.getUsername());
		int rs = orderService.addReservation(authenticatedUser, vo);
		if (rs == -1)
			return ApiResponse.createFailApiResponse(ApiResponseMessage.RECOMMENDER_NOT_FOUND);
		return ApiResponse.createSuccessApiResponse(rs);
	}

	@ApiOperation(notes = "用户在订单处于咨询状态时修改经理人，返回ApiResponse。如果ApiResponse为false则表示该经理人用户不存在。"
			+ "返回404表示此订单不存在。返回403表示此时订单不处于咨询状态或者订单所有者与token代表的用户不相符。", value = "")
	@PutMapping("/{id}/manager")
	@PreAuthorize("hasAnyRole('USER', 'MANAGER')")
	public ApiResponse<String> putManager(@ApiParam("订单id") @PathVariable int id,
			@ApiParam("经理人用户名") @RequestParam String username,
			@ApiIgnore @AuthenticationPrincipal UserContext context) {
		SimplifiedUser authenticatedUser = new SimplifiedUser(context.getUserId(), context.getUsername());
		boolean rs = orderService.fillManager(authenticatedUser, id, username);
		if (rs)
			return ApiResponse.SUCCESSFUL_RESPONSE_WITHOUT_MESSAGE;
		return ApiResponse.createFailApiResponse(ApiResponseMessage.MANAGER_NOT_FOUND);
	}

	@ApiOperation(notes = "用户在订单处于咨询状态时修改酒席日期，返回Boolean。日期格式：yyyy.MM.dd"
			+ "返回404表示此订单不存在。返回403表示此时订单不处于咨询状态或者订单所有者与token代表的用户不相符。", value = "")
	@PutMapping("/{id}/feasting_date")
	@PreAuthorize("hasAnyRole('USER', 'MANAGER')")
	public boolean putFeastingDate(@ApiParam("订单id") @PathVariable int id,
			@ApiParam("确定的酒席日期") @RequestParam String value, @ApiIgnore @AuthenticationPrincipal UserContext context) {
		SimplifiedUser authenticatedUser = new SimplifiedUser(context.getUserId(), context.getUsername());
		try {
			if (value.indexOf("-") == -1) {
				dateFormatter.parseToDateWithDefaultDateFormat(value);
			} else {
				for (String date : value.split("-")) {
					dateFormatter.parseToDateWithDefaultDateFormat(date);
				}
			}
		} catch (ParseException e) {
			throw new InvalidRequestParametersException(
					"The date format must be " + dateFormatter.getDefaultDateFormat() + "!");
		}
		return orderService.fillFeastingDate(authenticatedUser, id, value);
	}

	@ApiOperation(notes = "用户根据订单Id获得此笔订单的返现比例，返现比例不存在则返回null。<br>"
			+ "返现比例中的percentageForCustomer(double)代表百分比中的数值，例如percentageForCustomer=0.3，表示客户获得的返现比例为0.3%<br>"
			+ "返回404表示该笔订单不存在，返回403表示token代表的用户无权限查看此订单", value = "")
	@GetMapping("/{id}/cashback_strategy")
	@PreAuthorize("hasAnyRole('USER', 'MANAGER', 'ADMIN')")
	public CashbackStrategyCustomizationDTO getCashbackStrategy(@ApiParam("订单id") @PathVariable("id") int orderId,
			@ApiIgnore @AuthenticationPrincipal UserContext context) {
		SimplifiedUser authenticatedUser = new SimplifiedUser(context.getUserId(), context.getUsername(),
				context.getRole());
		return orderService.findCashbackStrategyByOrderId(orderId, authenticatedUser).orElse(null);
	}

	@ApiOperation(notes = "管理员为指定订单设置返现比例。<br>"
			+ "返现比例中的percentageForCustomer(double)代表百分比中的数值，例如percentageForCustomer=0.3，表示客户获得的返现比例为0.3%<br>"
			+ "返回404表示该笔订单不存在", value = "")
	@PutMapping("/{id}/cashback_strategy")
	@PreAuthorize("hasRole('ADMIN')")
	public boolean customizeCashbackStrategy(@ApiParam("订单id") @PathVariable("id") int orderId,
			@RequestBody CashbackStrategyVO cashbackStrategy) {
		return orderService.customizeCashbackStrategy(orderId, cashbackStrategy);
	}

	@ApiOperation(notes = "用户根据订单Id获得此笔订单的返现详情，包括该笔订单的订单金额和返现比例。<br>"
			+ "返回404表示该笔订单不存在，返回403表示token代表的用户无权限查看此订单", value = "")
	@GetMapping("/{id}/cashback")
	@PreAuthorize("hasAnyRole('USER', 'MANAGER', 'ADMIN')")
	public CashbackDTO getCashback(@ApiParam("订单id") @PathVariable("id") int orderId,
			@ApiIgnore @AuthenticationPrincipal UserContext context) {
		SimplifiedUser authenticatedUser = new SimplifiedUser(context.getUserId(), context.getUsername(),
				context.getRole());
		return orderService.findCashbackByOrderId(orderId, authenticatedUser);
	}

	@ApiOperation(notes = "管理员修改订单状态，返回修改后的OrderStatus。无参数表示使订单进入下一个状态（状态集合：CANCELED, CONSULTING, RESERVED, FEASTED, CASHBACK, TO_BE_REVIEWD, DONE;）。"
			+ "有参数value=CANCELED表示取消订单。返回404表示此订单不存在。返回403表示当前状态已是最后一个状态，因此操作失败。返回400表示参数传递错误", value = "")
	@PutMapping("/{id}/status")
	@PreAuthorize("hasRole('ADMIN')")
	public OrderStatus moveToNextStatus(@ApiParam("订单id") @PathVariable int id,
			@ApiParam("可选参数，暂时值只能为CANCELED") @RequestParam(value = "value", required = false) OrderStatus.ModifiableOrderStatus status) {
		if (status != null) {
			return orderService.updateStatus(id, status.toOrderStatus());
		}
		return orderService.moveToNextStatus(id);
	}

	@ApiOperation(notes = "用户上传合同照片，返回boolean。返回404表示此订单不存在。返回403表示此订单不处于商议/看场地状态或者订单所有者与token代表的用户不相符。", value = "")
	@PostMapping("/{id}/contract")
	@PreAuthorize("hasAnyRole('USER', 'MANAGER')")
	public boolean uploadContract(@ApiParam("订单id") @PathVariable int id, @ApiParam("String deleted（文件名与文件名使用;连接）， File[] files") PostContractVO vo,
			@ApiIgnore @AuthenticationPrincipal UserContext context) {
		if (vo.getFiles() != null && vo.getFiles().length > VOConstraints.MAX_SIZE_OF_CONTRACT)
			throw new BadRequestException(
					"The size of contract files must smaller than " + VOConstraints.MAX_SIZE_OF_CONTRACT);
		SimplifiedUser authenticatedUser = new SimplifiedUser(context.getUserId(), context.getUsername());
		return orderService.uploadContract(authenticatedUser, id, vo);
	}

	@ApiOperation(notes = "用户上传支付凭证，返回boolean。返回404表示此订单不存在。返回403表示此订单不处于已预订状态或者订单所有者与token代表的用户不相符。", value = "")
	@PostMapping("/{id}/payment")
	@PreAuthorize("hasAnyRole('USER', 'MANAGER')")
	public boolean uploadPayment(@ApiParam("订单id") @PathVariable int id,
			@ApiParam("payment对象，包含三个属性：double amountPaid，String deleted（文件名与文件名使用;连接）， File[] files") @Valid PostPaymentVO postPaymentVO,
			@AuthenticationPrincipal UserContext context) {
		if (postPaymentVO.getFiles() != null
				&& postPaymentVO.getFiles().length > VOConstraints.MAX_SIZE_OF_PAYMENT_FILES)
			throw new BadRequestException(
					"The size of payment files must smaller than " + VOConstraints.MAX_SIZE_OF_PAYMENT_FILES);
		SimplifiedUser authenticatedUser = new SimplifiedUser(context.getUserId(), context.getUsername());
		return orderService.fillPayment(authenticatedUser, id, postPaymentVO);
	}

	@ApiOperation(notes = "用户根據訂單ID獲得訂單詳細信息，ApprovalStatus:(PENDING, APPROVED, DECLINED)", value = "")
	@GetMapping("/{id}")
	@PreAuthorize("hasAnyRole('USER', 'MANAGER', 'ADMIN')")
	public OrderDTO findById(@ApiParam("订单id") @PathVariable int id,
			@ApiIgnore @AuthenticationPrincipal UserContext context) {
		SimplifiedUser authenticatedUser = new SimplifiedUser(context.getUserId(), context.getUsername(),
				context.getRole());
		return orderService.findOrderById(authenticatedUser, id)
				.orElseThrow(() -> new ResourceNotFoundException("Order with id: " + id + " doesn't exist"));
	}

	@ApiOperation(notes = "管理员审批用户上传的支付凭证，包含一个必填参数status.例子："
			+ "PUT /{id}/payment?status=APPROVED。status值集合:(APPROVED, DECLINED)", value = "")
	@PutMapping("/{id}/payment")
	@PreAuthorize("hasRole('ADMIN')")
	public boolean approvePayment(@ApiParam("订单id") @PathVariable int id,
			@ApiParam("status:(APPROVED, DECLINED)") @RequestParam("status") ModifiableApprovalStatus approvalStatus,
			@AuthenticationPrincipal UserContext context) {
		return orderService.updateOrderPaymentApprovalStatus(id, approvalStatus);
	}

	@ApiOperation(notes = "管理员审批用户上传的合同文件，包含一个必填参数status.例子："
			+ "PUT /{id}/contract?status=APPROVED。status值集合:(APPROVED, DECLINED)", value = "")
	@PutMapping("/{id}/contract")
	@PreAuthorize("hasRole('ADMIN')")
	public boolean approveContract(@ApiParam("订单id") @PathVariable int id,
			@ApiParam("status:(APPROVED, DECLINED)") @RequestParam("status") ModifiableApprovalStatus approvalStatus,
			@ApiIgnore @AuthenticationPrincipal UserContext context) {
		return orderService.updateOrderContractApprovalStatus(id, approvalStatus);
	}

	@ApiOperation(notes = "管理员获得订单列表，GET参数如下(GET参数均为可选项，可以不填)：<br>" + "since=2017.10.01  表示订单时间大于等于2017.10.01 <br>"
			+ "until=2017.10.02  表示订单时间小于等于2017.10.02 <br>" + "order_number=103478313233 订单号<br>"
			+ "status=CONSULTING,RESERVED 多个状态使用逗号连接。状态集合：{ CANCELED, CONSULTING, RESERVED, FEASTED, CASHBACK, TO_BE_REVIEWD, DONE } <br>"
			+ "customer=ivan1 客户的用户名<br>" + "manager=manager1 经理人的用户名<br>" + "recommender=ivan2 推荐人的用户名<br>"
			+ "contact=Simon 订单中的联系人<br>" + "hotel=hotel1 酒店名称<br>" + "banquet_hall=bh 宴会厅名称<br>"
			+ "contract_status=PENDING 合同审批状态，包括 ：{ PENDING, APPROVED, DECLINED }<br>"
			+ "payment_status=PENDING 支付凭证审批状态，包括：{ PENDING, APPROVED, DECLINED }<br>" + "page=0 页码<br>"
			+ "size=20 每页返回的订单数量，默认值为20<br>" + "sort=orderTime,desc 排序，默认按照orderTime降序排列。所有返回值中方的属性均可指定为排序属性。<br>"
			+ "<br>" + "Response是一个Page对象，包含以下属性：<br>" + "content: 一个包含订单对象的数组(orderNumber是订单号)<br>"
			+ "first: 当前页是否是第一页<br>" + "last: 当前页是否是最后一页<br>" + "number: 当前页的页码<br>" + "numberOfElements: 当前页的订单数量<br>"
			+ "size: 每页的订单数量，即url中指定的size参数，默认为20<br>" + "sort: 当前页的排序规则，即url中指定的sort参数<br>"
			+ "totalElements: 符合查询条件的总的订单数量<br>" + "totalPages: 符合查询条件的总的页数<br>", value = "")
	@GetMapping
	@PreAuthorize("hasRole('ADMIN')")
	public Page<SimplifiedOrderForAdminDTO> listOrdersForAdmin(
			@ApiParam("订单时间的下限") @RequestParam(required = false) String since,
			@ApiParam("订单时间的上限") @RequestParam(required = false) String until,
			@ApiParam("订单号") @RequestParam(value = "order_number", required = false) String orderNumber,
			@ApiParam("订单状态集合") @RequestParam(value = "status", required = false) OrderStatus[] orderStatus,
			@ApiParam("客户的用户名") @RequestParam(required = false) String customer,
			@ApiParam("经理人的用户名") @RequestParam(required = false) String manager,
			@ApiParam("推介人的用户名") @RequestParam(required = false) String recommender,
			@ApiParam("联系人") @RequestParam(required = false) String contact,
			@ApiParam("酒店名称") @RequestParam(required = false) String hotel,
			@ApiParam("宴会厅名称") @RequestParam(value = "banquet_hall", required = false) String banquetHall,
			@ApiParam("合同审批状态") @RequestParam(value = "contract_status", required = false) ApprovalStatus contractApprovalStatus,
			@ApiParam("支付凭证审批状态") @RequestParam(value = "payment_status", required = false) ApprovalStatus paymentApprovalStatus,
			@ApiParam("分页参数，可选。例子：page=0&size=10&sort=orderTime,desc。"
					+ "默认情况下size=20, sort=orderTime,desc.") @PageableDefault(value = PaginationConfig.ORDERS_PER_PAGE_FOR_AMDMIN, sort = {
							"orderTime" }, direction = Sort.Direction.DESC) Pageable pageable) {
		Long orderNumberLong = null;
		if(orderNumber!=null){
			try {
				orderNumberLong = Long.parseLong(orderNumber);
			} catch (NumberFormatException e) {
				orderNumberLong = -1L;
			}
		}
		OrderQueryVO orderQueryVO = new OrderQueryVO(parseToTimestampWithDefaultDateFormat(since),
				parseToTimestampWithDefaultDateFormat(until), orderStatus, orderNumberLong, contractApprovalStatus,
				paymentApprovalStatus, customer, manager, recommender, contact, hotel, banquetHall);
//		OrderQueryVO orderQueryVO = new OrderQueryVO();
		return orderService.listOrdersForAdmin(orderQueryVO, pageable);
	}

	@ApiOperation(notes = "用户对订单进行评论，返回新增评论的id。返回404表示此订单不存在。返回403表示此订单已经被评价过或者token代表的用户无权限", value = "")
	@PostMapping("/{id}/reviews")
	@PreAuthorize("hasAnyRole('USER', 'MANAGER')")
	public int addReviews(@ApiParam("订单id") @PathVariable int id,
			@ApiParam("Review对象，包含两个属性：double rating(0.0-5.0)，String review(评论)") @Valid @RequestBody PostReviewVO postReviewVO,
			@AuthenticationPrincipal UserContext context) {
		SimplifiedUser authenticatedUser = new SimplifiedUser(context.getUserId(), context.getUsername(), context.getRole());
		return reviewService.addReviews(id, authenticatedUser, postReviewVO);
	}

	private Long parseToTimestampWithDefaultDateFormat(String date) {
		if (StringUtils.isEmpty(date))
			return null;
		try {
			return dateFormatter.parseToDateWithDefaultDateFormat(date).getTime();
		} catch (ParseException e) {
			throw new InvalidRequestParametersException(
					"The date format must be " + dateFormatter.getDefaultDateFormat() + "!");
		}
	}
}
